package birdry.box2d
{
	
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.Joints.b2MouseJoint;
	import Box2D.Dynamics.Joints.b2MouseJointDef;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2World;
	
	import birdry.box2d.B2Util;
	
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	
	
	
	
	
	/**
	 * Box2D - Basic View
	 */
	public class B2BasicView extends Sprite
	{
		
		//--------------------------------------------------------------------------
		//
		//	Public Variables
		//
		//--------------------------------------------------------------------------
		
		/**
		 * VelocityIterations
		 */
		public var velocityIterations:int	= 10;
		
		/**
		 * PositionIterations
		 */
		public var positionIterations:int	= 10;
		
		/**
		 * Mouse Joint Max Force
		 */
		public var mouseJointMaxForce:Number = 300;
		
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//	Protected Variables
		//
		//--------------------------------------------------------------------------
		
		/**
		 * Box2D World
		 */
		protected var world:b2World;
		
		
		
		/**
		 * Mouse Down Check for Basic Mouse Joint
		 */
		protected var bMouseDown:Boolean;
		
		/**
		 * MouseJoint for Basic Mouse Joint
		 */
		protected var mouseJoint:b2MouseJoint;
		
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//	Constructor
		//
		//--------------------------------------------------------------------------
		
		/**
		 * Constructor
		 */
		public function B2BasicView( useBasicMouseJoint:Boolean=true, doSleep:Boolean=true )
		{
			super();
			
			this._doSleep	= doSleep;
			
			this.world	= new b2World( this._gravity, this._doSleep );
			
			this._useBasicMouseJoint	= useBasicMouseJoint;
			
			if( this._useBasicMouseJoint )
			{
				this.addEventsForBasicMouseJoint();
			}
		}
		
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//	Public Function
		//
		//--------------------------------------------------------------------------
		
		/**
		 * StartRendering
		 */
		public function startRendering() : void
		{
			if( !this.hasEventListener( Event.ENTER_FRAME ) )
			{
				this.addEventListener( Event.ENTER_FRAME, onRendering );
			}
		}
		
		
		
		/**
		 * StopRendering
		 */
		public function stopRendering() : void
		{
			if( this.hasEventListener( Event.ENTER_FRAME ) )
			{
				this.removeEventListener( Event.ENTER_FRAME, onRendering );
			}
		}
		
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//	Protected Function
		//
		//--------------------------------------------------------------------------
		
		/**
		 * Add Events For Basic Mouse Joint
		 */
		protected function addEventsForBasicMouseJoint() : void
		{
			if( this.stage )
			{
				this.basicMouseJointEventsSetting( null );
			}
			else
			{
				this.addEventListener( Event.ADDED_TO_STAGE, basicMouseJointEventsSetting );
			}
		}
		
		
		
		/**
		 * Basic Mouse Joint Events Setting
		 * 
		 * @param e
		 * 
		 */		
		protected function basicMouseJointEventsSetting( e:Event ) : void
		{
			this.stage.addEventListener( MouseEvent.MOUSE_DOWN, onStageMouseDown );
			this.stage.addEventListener( MouseEvent.MOUSE_UP, onStageMouseUp );
		}
		
		
		
		/**
		 * Stage Mouse Down Event Handler
		 * 
		 * @param e
		 * 
		 */		
		protected function onStageMouseDown( e:MouseEvent ) : void
		{
			this.bMouseDown	= true;
			
			if( !this.mouseJoint )
			{
				var body:b2Body	= B2Util.getBodyAtMouse( this.world, this.stage.mouseX, this.stage.mouseY );
				
				if( body )
				{
					var mouseJointDef:b2MouseJointDef	= new b2MouseJointDef();
					mouseJointDef.bodyA	= this.world.GetGroundBody();
					mouseJointDef.bodyB	= body;
					mouseJointDef.target.Set( this.stage.mouseX/30, this.stage.mouseY/30 );
					mouseJointDef.maxForce	= this.mouseJointMaxForce * body.GetMass();
					
					this.mouseJoint	= this.world.CreateJoint( mouseJointDef ) as b2MouseJoint;
					
					body.SetAwake( true );
				}
			}
		}
		
		
		
		/**
		 * Stage Mouse Up Event Handler
		 * 
		 * @param e
		 * 
		 */		
		protected function onStageMouseUp( e:MouseEvent ) : void
		{
			this.bMouseDown	= false;
		}
		
		
		
		/**
		 * Check Mouse Events for Basic Mouse Joint
		 */		
		protected function checkMouseEvents() : void
		{
			if( this.mouseJoint )
			{
				var vec2:b2Vec2	= new b2Vec2();
				vec2.Set( this.stage.mouseX/30, this.stage.mouseY/30 );
				
				this.mouseJoint.SetTarget( vec2 );
			}
			
			if( !this.bMouseDown && this.mouseJoint )
			{
				this.world.DestroyJoint( this.mouseJoint );
				this.mouseJoint	= null;
			}
		}
		
		
		
		/**
		 * Rendering
		 */
		protected function onRendering( e:Event ) : void
		{
			this.world.Step( 1 / this.stage.frameRate, this.velocityIterations, this.positionIterations );
			
			this.world.ClearForces();
			
			this.world.DrawDebugData();
			
			var userData:DisplayObject;
			
			for( var body:b2Body	= this.world.GetBodyList(); body; body	= body.GetNext() )
			{
				userData	= body.GetUserData() as DisplayObject;
				
				if( userData )
				{
					userData.x			= body.GetPosition().x * 30;
					userData.y			= body.GetPosition().y * 30;
					userData.rotation	= body.GetAngle() * ( 180 / Math.PI );
				}
			}
			
			if( this.useBasicMouseJoint )
			{
				this.checkMouseEvents();
			}
		}
		
		
		
		
		
		//--------------------------------------------------------------------------
		//
		//	getter / setter
		//
		//--------------------------------------------------------------------------
		
		/**
		 * Box2D World, Gravity
		 */
		private var _gravity:b2Vec2	= new b2Vec2( 0, 10 );
		
		/**
		 * @private
		 */
		public function get gravity() : b2Vec2
		{
			return this._gravity;
		}
		
		/**
		 * Box2D World, Gravity
		 * 
		 * @param gravity
		 * 
		 */
		public function set gravity( gravity:b2Vec2 ) : void
		{
			this._gravity	= gravity;
			
			this.world.SetGravity( this._gravity );
		}
		
		
		
		//-------------------------------------
		//	Do Sleep
		//-------------------------------------
		private var _doSleep:Boolean;
		
		/**
		 * Box2D World, DoSleep
		 * 
		 * @return
		 */
		
		public function get doSleep() : Boolean
		{
			return this._doSleep;
		}
		
		
		
		//-------------------------------------
		//	Use Basic Mouse Joint
		//-------------------------------------
		private var _useBasicMouseJoint:Boolean;
		
		/**
		 * Use Basic Mouse joint
		 * 
		 * @return 
		 * 
		 */		
		public function get useBasicMouseJoint() : Boolean
		{
			return _useBasicMouseJoint;
		}
		
		
		
	}
}